到目前為止,我們把 API 的錯誤訊息改成 { code, message }
格式,這樣 UI 可以更好地分流處理。但這只是「能看懂」。如果再往前一步,錯誤就不只是障礙,而是能幫開發者的工具。
我的想法是靠以下三把螺絲(三部)協助。
每一種錯誤對應到固定的 ErrorCode
,像是 InputLengthMismatch
、BadOps
、Unsupported
。這些碼值不會亂改,讓上層應用敢放心 switch / if 判斷,不怕某天換名字。
message
是給人看的,不需要永遠一樣,但必須直白。
例如:
InputLengthMismatch
→ 「像素長度不對,請確認 wh4」BadOps
→ 「ops JSON 格式錯誤」當錯誤發生時,開發者不用再反查 spec,就能第一眼知道要修哪裡。
錯誤訊息不只告訴你「錯了」,還能指引「怎麼做對」。
例如:
Unsupported
→ message 裡帶「目前支援 grayscale / bc / blur / conv3x3 / unsharp / edge_sobel」BadOps
→ message 裡提示「檢查是不是漏了 kind 欄位」我們之前有 js_err
,現在再加一點「建議」在 message
裡。
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
enum ErrorCode {
InputLengthMismatch,
BadOps,
Unsupported,
Internal,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
struct JsError {
code: ErrorCode,
message: String,
}
fn js_err(code: ErrorCode, msg: &str) -> JsValue {
let err = JsError { code, message: msg.to_string() };
serde_wasm_bindgen::to_value(&err).unwrap_or_else(|_| JsValue::from_str(msg))
}
#[wasm_bindgen]
pub fn apply_pipeline(input: &[u8], w: u32, h: u32, ops: &JsValue) -> Result<Vec<u8>, JsValue> {
let expected = (w as usize) * (h as usize) * 4;
if input.len() != expected {
return Err(js_err(
ErrorCode::InputLengthMismatch,
&format!("input length mismatch: expected {} bytes (w*h*4)", expected),
));
}
let ops: Vec<Op> = serde_wasm_bindgen::from_value(ops.clone())
.map_err(|e| js_err(
ErrorCode::BadOps,
&format!("bad ops JSON: {e}. 提示: 確認每個物件都有 kind 欄位")
))?;
// 假設遇到不支援的操作
for op in &ops {
match op {
Op::Grayscale | Op::BrightnessContrast{..} | Op::Blur{..}
| Op::Conv3x3{..} | Op::Unsharp{..} | Op::EdgeSobel => {}
//_ => return Err(js_err(ErrorCode::Unsupported, "unsupported op. 支援: grayscale/bc/blur/conv3x3/unsharp/edge_sobel")),
}
}
Ok(apply_pipeline_inner(input, w, h, &ops))
}
function showWasmError(e: any) {
if (e && e.code && e.message) {
switch (e.code) {
case "INPUT_LENGTH_MISMATCH":
alert("尺寸不對:" + e.message)
break
case "BAD_OPS":
alert("參數錯誤:" + e.message)
break
case "UNSUPPORTED":
console.warn("遇到不支援的操作 → " + e.message)
break
default:
console.error("WASM Internal Error:", e.message)
}
} else {
console.error("Unknown error", e)
}
}